[ACM] Check a domain's authorization to run.
authorkfraser@localhost.localdomain <kfraser@localhost.localdomain>
Fri, 27 Jul 2007 08:01:15 +0000 (09:01 +0100)
committerkfraser@localhost.localdomain <kfraser@localhost.localdomain>
Fri, 27 Jul 2007 08:01:15 +0000 (09:01 +0100)
A domain is only authorized to run if it has a superset of Simple Type
Enforcement Types in its VM label compared to that of Domain-0, which
itself may not have all STEs available in a policy. This patch adds a
check for this into Xend and the necessary code support into Xen.

Signed-off-by: Stefan Berger <stefanb@us.ibm.com>
tools/python/xen/lowlevel/acm/acm.c
tools/python/xen/util/security.py
tools/python/xen/xend/XendDomainInfo.py
xen/acm/acm_chinesewall_hooks.c
xen/acm/acm_core.c
xen/acm/acm_policy.c
xen/acm/acm_simple_type_enforcement_hooks.c
xen/include/acm/acm_core.h
xen/include/acm/acm_hooks.h
xen/include/public/acm.h

index f8f024f66f88248da6d8bb3b12c4b87bfc9e1481..9b59ea48ed844fc0c0ab669e4724e5b6420de895 100644 (file)
@@ -148,9 +148,10 @@ static PyObject *getdecision(PyObject * self, PyObject * args)
     char *arg1_name, *arg1, *arg2_name, *arg2, *decision = NULL;
     struct acm_getdecision getdecision;
     int xc_handle, rc;
+    uint32_t hooktype;
 
-    if (!PyArg_ParseTuple(args, "ssss", &arg1_name,
-                          &arg1, &arg2_name, &arg2)) {
+    if (!PyArg_ParseTuple(args, "ssssi", &arg1_name,
+                          &arg1, &arg2_name, &arg2, &hooktype)) {
         return NULL;
     }
 
@@ -163,7 +164,7 @@ static PyObject *getdecision(PyObject * self, PyObject * args)
     (strcmp(arg2_name, "domid") && strcmp(arg2_name, "ssidref")))
         return NULL;
 
-    getdecision.hook = ACMHOOK_sharing;
+    getdecision.hook = hooktype;
     if (!strcmp(arg1_name, "domid")) {
         getdecision.get_decision_by1 = ACM_GETBY_domainid;
         getdecision.id1.domainid = atoi(arg1);
index 5183ed2c989e81a5bd036ff15a807af10afaac32..47d51cb20aabe9f96386c1df210417026da81e88 100644 (file)
@@ -62,6 +62,10 @@ empty_line_re = re.compile("^\s*$")
 binary_name_re = re.compile(".*[chwall|ste|chwall_ste].*\.bin", re.IGNORECASE)
 policy_name_re = re.compile(".*[chwall|ste|chwall_ste].*", re.IGNORECASE)
 
+#decision hooks known to the hypervisor
+ACMHOOK_sharing = 1
+ACMHOOK_authorization = 2
+
 #other global variables
 NULL_SSIDREF = 0
 
@@ -453,7 +457,8 @@ def get_decision(arg1, arg2):
         err("Invalid id or ssidref type, string or int required")
 
     try:
-        decision = acm.getdecision(arg1[0], arg1[1], arg2[0], arg2[1])
+        decision = acm.getdecision(arg1[0], arg1[1], arg2[0], arg2[1],
+                                   ACMHOOK_sharing)
     except:
         err("Cannot determine decision.")
 
@@ -463,6 +468,21 @@ def get_decision(arg1, arg2):
         err("Cannot determine decision (Invalid parameter).")
 
 
+def has_authorization(ssidref):
+    """ Check if the domain with the given ssidref has authorization to
+        run on this system. To have authoriztion dom0's STE types must
+        be a superset of that of the domain's given through its ssidref.
+    """
+    rc = True
+    dom0_ssidref = int(acm.getssid(0)['ssidref'])
+    decision = acm.getdecision('ssidref', str(dom0_ssidref),
+                               'ssidref', str(ssidref),
+                               ACMHOOK_authorization)
+    if decision == "DENIED":
+        rc = False
+    return rc
+
+
 def hv_chg_policy(bin_pol, del_array, chg_array):
     """
         Change the binary policy in the hypervisor
index 3ce18bc96fa65ef5af41e30cf89c7476d64853a0..0fe545aa310fcb9b4b4dd14d8569c69e4a770b3d 100644 (file)
@@ -1460,9 +1460,13 @@ class XendDomainInfo:
         # allocation of 1MB. We free up 2MB here to be on the safe side.
         balloon.free(2*1024) # 2MB should be plenty
 
-        ssidref = security.calc_dom_ssidref_from_info(self.info)
-        if ssidref == 0 and security.on():
-            raise VmError('VM is not properly labeled.')
+        ssidref = 0
+        if security.on():
+            ssidref = security.calc_dom_ssidref_from_info(self.info)
+            if ssidref == 0:
+                raise VmError('VM is not properly labeled.')
+            if security.has_authorization(ssidref) == False:
+                raise VmError("VM is not authorized to run.")
 
         try:
             self.domid = xc.domain_create(
index be68a43f48bca63c0b1460a344274da04109ee64..90e20a19d0a15482d4606b42e9b6ac6e2e6e8869 100644 (file)
@@ -685,6 +685,7 @@ struct acm_operations acm_chinesewall_ops = {
     .fail_grant_setup = NULL,
     /* generic domain-requested decision hooks */
     .sharing = NULL,
+    .authorization = NULL,
 
     .is_default_policy = chwall_is_default_policy,
 };
index 72039549099da47d27134653189b972d6c7cc49b..a989d4c58111901b6bb5a26b28df084db45b6fb3 100644 (file)
@@ -314,26 +314,7 @@ acm_init(char *policy_start,
     return ret;
 }
 
-int
-acm_init_domain_ssid(domid_t id, ssidref_t ssidref)
-{
-    struct domain *subj = rcu_lock_domain_by_id(id);
-    int ret;
-    if (subj == NULL)
-    {
-        printk("%s: ACM_NULL_POINTER ERROR (id=%x).\n", __func__, id);
-        return ACM_NULL_POINTER_ERROR;
-    }
-
-    ret = acm_init_domain_ssid_new(subj, ssidref);
-
-    rcu_unlock_domain(subj);
-
-    return ret;
-}
-
-int acm_init_domain_ssid_new(struct domain *subj, ssidref_t ssidref)
+int acm_init_domain_ssid(struct domain *subj, ssidref_t ssidref)
 {
     struct acm_ssid_domain *ssid;
     int ret1, ret2;
@@ -374,10 +355,6 @@ int acm_init_domain_ssid_new(struct domain *subj, ssidref_t ssidref)
         return ACM_INIT_SSID_ERROR;
     }
 
-    write_lock(&ssid_list_rwlock);
-    list_add(&ssid->node, &ssid_list);
-    write_unlock(&ssid_list_rwlock);
-
     printkd("%s: assigned domain %x the ssidref=%x.\n",
            __func__, subj->domain_id, ssid->ssidref);
     return ACM_OK;
@@ -399,10 +376,6 @@ acm_free_domain_ssid(struct acm_ssid_domain *ssid)
         acm_secondary_ops->free_domain_ssid(ssid->secondary_ssid);
     ssid->secondary_ssid = NULL;
 
-    write_lock(&ssid_list_rwlock);
-    list_del(&ssid->node);
-    write_unlock(&ssid_list_rwlock);
-
     xfree(ssid);
     printkd("%s: Freed individual domain ssid (domain=%02x).\n",
             __func__, id);
index 08d2f823b5c0b47ec7562000b99b8fca6d06fd38..5fd57e872bc3986e6e26219d9d0def89d2e174d0 100644 (file)
@@ -438,6 +438,10 @@ acm_get_decision(ssidref_t ssidref1, ssidref_t ssidref2, u32 hook)
         ret = acm_sharing(ssidref1, ssidref2);
         break;
 
+    case ACMHOOK_authorization:
+        ret = acm_authorization(ssidref1, ssidref2);
+        break;
+
     default:
         /* deny */
         break;
index 1e078e334779e5eaed43af8c508abec219138a5e..a4fcbd488dbf7216fe4aefaaa170f73a9a49865c 100644 (file)
@@ -38,15 +38,16 @@ ssidref_t dom0_ste_ssidref = 0x0001;
 /* local cache structures for STE policy */
 struct ste_binary_policy ste_bin_pol;
 
-static inline int have_common_type (ssidref_t ref1, ssidref_t ref2) {
+static inline int have_common_type (ssidref_t ref1, ssidref_t ref2)
+{
     int i;
 
     if ( ref1 >= 0 && ref1 < ste_bin_pol.max_ssidrefs &&
          ref2 >= 0 && ref2 < ste_bin_pol.max_ssidrefs )
     {
         for( i = 0; i< ste_bin_pol.max_types; i++ )
-            if ( ste_bin_pol.ssidrefs[ref1*ste_bin_pol.max_types + i] &&
-                 ste_bin_pol.ssidrefs[ref2*ste_bin_pol.max_types + i])
+            if ( ste_bin_pol.ssidrefs[ref1 * ste_bin_pol.max_types + i] &&
+                 ste_bin_pol.ssidrefs[ref2 * ste_bin_pol.max_types + i])
             {
                 printkd("%s: common type #%02x.\n", __func__, i);
                 return 1;
@@ -55,6 +56,26 @@ static inline int have_common_type (ssidref_t ref1, ssidref_t ref2) {
     return 0;
 }
 
+static inline int is_superset(ssidref_t ref1, ssidref_t ref2)
+{
+    int i;
+
+    if ( ref1 >= 0 && ref1 < ste_bin_pol.max_ssidrefs &&
+         ref2 >= 0 && ref2 < ste_bin_pol.max_ssidrefs )
+    {
+        for( i = 0; i< ste_bin_pol.max_types; i++ )
+            if (!ste_bin_pol.ssidrefs[ref1 * ste_bin_pol.max_types + i] &&
+                 ste_bin_pol.ssidrefs[ref2 * ste_bin_pol.max_types + i])
+            {
+                return 0;
+            }
+    } else {
+        return 0;
+    }
+    return 1;
+}
+
+
 /* Helper function: return = (subj and obj share a common type) */
 static int share_common_type(struct domain *subj, struct domain *obj)
 {
@@ -609,6 +630,7 @@ ste_pre_domain_create(void *subject_ssid, ssidref_t ssidref)
 {      
     /* check for ssidref in range for policy */
     ssidref_t ste_ssidref;
+
     traceprintk("%s.\n", __func__);
 
     read_lock(&acm_bin_pol_rwlock);
@@ -832,6 +854,15 @@ ste_sharing(ssidref_t ssidref1, ssidref_t ssidref2)
     return (hct ? ACM_ACCESS_PERMITTED : ACM_ACCESS_DENIED);
 }
 
+static int
+ste_authorization(ssidref_t ssidref1, ssidref_t ssidref2)
+{
+    int iss = is_superset(
+        GET_SSIDREF(ACM_SIMPLE_TYPE_ENFORCEMENT_POLICY, ssidref1),
+        GET_SSIDREF(ACM_SIMPLE_TYPE_ENFORCEMENT_POLICY, ssidref2));
+    return (iss ? ACM_ACCESS_PERMITTED : ACM_ACCESS_DENIED);
+}
+
 static int
 ste_is_default_policy(void)
 {
@@ -867,6 +898,7 @@ struct acm_operations acm_simple_type_enforcement_ops = {
     .pre_grant_setup        = ste_pre_grant_setup,
     .fail_grant_setup       = NULL,
     .sharing                = ste_sharing,
+    .authorization          = ste_authorization,
 
     .is_default_policy      = ste_is_default_policy,
 };
index 0045a1bca09f8d59e69147ee3dde95ad936be501..1db2f321203d20ff750530add52d246ce614bbc4 100644 (file)
@@ -153,8 +153,7 @@ static inline int acm_array_append_tuple(struct acm_sized_buffer *buf,
 }
 
 /* protos */
-int acm_init_domain_ssid(domid_t id, ssidref_t ssidref);
-int acm_init_domain_ssid_new(struct domain *, ssidref_t ssidref);
+int acm_init_domain_ssid(struct domain *, ssidref_t ssidref);
 void acm_free_domain_ssid(struct acm_ssid_domain *ssid);
 int acm_init_binary_policy(u32 policy_code);
 int acm_set_policy(XEN_GUEST_HANDLE_64(void) buf, u32 buf_size);
index 759a1a08432c17bf3adf3ac11af71d4f087f7137..becf5547531b350612cdc9d944d5299c8c0047a5 100644 (file)
@@ -112,7 +112,10 @@ struct acm_operations {
     int  (*pre_grant_setup)            (domid_t id);
     void (*fail_grant_setup)           (domid_t id);
     /* generic domain-requested decision hooks (can be NULL) */
-    int (*sharing)                     (ssidref_t ssidref1, ssidref_t ssidref2);
+    int (*sharing)                     (ssidref_t ssidref1,
+                                        ssidref_t ssidref2);
+    int (*authorization)               (ssidref_t ssidref1,
+                                        ssidref_t ssidref2);
     /* determine whether the default policy is installed */
     int (*is_default_policy)           (void);
 };
@@ -148,6 +151,8 @@ static inline int acm_is_policy(char *buf, unsigned long len)
 { return 0; }
 static inline int acm_sharing(ssidref_t ssidref1, ssidref_t ssidref2)
 { return 0; }
+static inline int acm_authorization(ssidref_t ssidref1, ssidref_t ssidref2)
+{ return 0; }
 static inline int acm_domain_create(struct domain *d, ssidref_t ssidref)
 { return 0; }
 static inline void acm_domain_destroy(struct domain *d)
@@ -157,6 +162,19 @@ static inline void acm_domain_destroy(struct domain *d)
 
 #else
 
+static inline void acm_domain_ssid_onto_list(struct acm_ssid_domain *ssid)
+{
+    write_lock(&ssid_list_rwlock);
+    list_add(&ssid->node, &ssid_list);
+    write_unlock(&ssid_list_rwlock);
+}
+
+static inline void acm_domain_ssid_off_list(struct acm_ssid_domain *ssid)
+{
+    write_lock(&ssid_list_rwlock);
+    list_del(&ssid->node);
+    write_unlock(&ssid_list_rwlock);
+}
 
 static inline int acm_pre_eventchannel_unbound(domid_t id1, domid_t id2)
 {
@@ -241,6 +259,7 @@ static inline void acm_domain_destroy(struct domain *d)
         if (acm_secondary_ops->domain_destroy != NULL)
             acm_secondary_ops->domain_destroy(ssid, d);
         /* free security ssid for the destroyed domain (also if null policy */
+        acm_domain_ssid_off_list(ssid);
         acm_free_domain_ssid((struct acm_ssid_domain *)(ssid));
     }
 }
@@ -250,13 +269,16 @@ static inline int acm_domain_create(struct domain *d, ssidref_t ssidref)
 {
     void *subject_ssid = current->domain->ssid;
     domid_t domid = d->domain_id;
-    int rc = 0;
+    int rc;
 
     read_lock(&acm_bin_pol_rwlock);
     /*
        To be called when a domain is created; returns '0' if the
        domain is allowed to be created, != '0' if not.
      */
+    rc = acm_init_domain_ssid(d, ssidref);
+    if (rc != ACM_OK)
+        goto error_out;
 
     if ((acm_primary_ops->domain_create != NULL) &&
         acm_primary_ops->domain_create(subject_ssid, ssidref, domid)) {
@@ -267,18 +289,17 @@ static inline int acm_domain_create(struct domain *d, ssidref_t ssidref)
         /* roll-back primary */
         if (acm_primary_ops->domain_destroy != NULL)
             acm_primary_ops->domain_destroy(d->ssid, d);
-        acm_free_domain_ssid(d->ssid);
         rc = ACM_ACCESS_DENIED;
     }
 
-    if (rc == 0) {
-        rc = acm_init_domain_ssid_new(d, ssidref);
-
-        if (rc != ACM_OK) {
-            acm_domain_destroy(d);
-        }
+    if ( rc == ACM_OK )
+    {
+        acm_domain_ssid_onto_list(d->ssid);
+    } else {
+        acm_free_domain_ssid(d->ssid);
     }
 
+error_out:
     read_unlock(&acm_bin_pol_rwlock);
     return rc;
 }
@@ -297,6 +318,19 @@ static inline int acm_sharing(ssidref_t ssidref1, ssidref_t ssidref2)
 }
 
 
+static inline int acm_authorization(ssidref_t ssidref1, ssidref_t ssidref2)
+{
+    if ((acm_primary_ops->authorization != NULL) &&
+        acm_primary_ops->authorization(ssidref1, ssidref2))
+        return ACM_ACCESS_DENIED;
+    else if ((acm_secondary_ops->authorization != NULL) &&
+             acm_secondary_ops->authorization(ssidref1, ssidref2)) {
+        return ACM_ACCESS_DENIED;
+    } else
+        return ACM_ACCESS_PERMITTED;
+}
+
+
 extern int acm_init(char *policy_start, unsigned long policy_len);
 
 /* Return true iff buffer has an acm policy magic number.  */
index ef62da0201301995fc749455b9be024d7a9e094a..7f37ff946964fdde69215852e4cab8f940aeb820 100644 (file)
@@ -99,8 +99,9 @@
 typedef uint32_t ssidref_t;
 
 /* hooks that are known to domains */
-#define ACMHOOK_none    0
-#define ACMHOOK_sharing 1
+#define ACMHOOK_none          0
+#define ACMHOOK_sharing       1
+#define ACMHOOK_authorization 2
 
 /* -------security policy relevant type definitions-------- */